#ifndef	__sel_textstream_h__
#define	__sel_textstream_h__

#include <stdio.h>

// FIXME: This should be determined by configure !
// #define HAVE_VSNPRINTF ( defined __USE_BSD || defined __USE_ISOC99 || defined __USE_UNIX_98 )

namespace SEL
{

//! A simple stream-like wrapper for std::string.
/*!
  The purpose of this class is to simplify creating strings
  and writing variables to them. You can simply create a
  TextStream and then use << operators for appending integers,
  floats, chars, etc.
 
  For more advanced formating you can use the appendFormat function
  but be aware of the maximum size of the buffer this function
  works with (default 4096 bytes). You can set the size of it
  by calling the setFormatBufferSize function.
 
  \sa appendFormat ()
  \sa setFormatBufferSize ()
 */
class TextStream
{
protected:
	std::string * m_str;
	signed int m_width;
	int m_precision;
	bool free_string;
	int m_format_buffer_size;
public:
	//! Create a new empty stream.
	TextStream ();

	//! Delete and free stream.
	~TextStream ();

	//! Create a new stream with an assigned value.
	/*!
          \param str - value to assign
         */
	TextStream (const std::string & str);

	//! Create a new stream with custom string.
        /*! 
	  The string is not freed on destruction.
	  Do not destroy the string while stream
	  still exists, because it may lead to crash.
	 
	  \param str - pointer to string
	 */
	TextStream (std::string * str);

	//! Set minimal width of a formated number.
	/*!
          Use width 0 for automatic width, positive width
          to align number to the right and negative to align number
          to the left.
         
          \param width - new width
          \sa width()
         */
	void setWidth (const signed int width);

	//! Return current minimal width of a formated number.
	unsigned int width () const;

	//! Set precision for floating point numbers.
	/*!
          This determines the number of digits after the
          floating point. If \a precision is -1 it will be
          determined automatically.
         
          \param precision - new precision
          \sa precision()
         */
	void setPrecision (const int precision);

	//! Return current precision.
	/*!
          \sa setPrecision()
         */
	int precision () const;

	//! Set the size of the format buffer.
	/*!
	  Format buffer is buffer where the function
	  appendFormat stores temporary formated text.
	 
	  \param size - the size of the buffer (in bytes)
	  \sa appendFormat ()
	 */
	void setFormatBufferSize (const int size);

	//! Return the size of the format buffer.
	/*!
	  \sa setFormatBufferSize()
	 */
	int formatBufferSize () const;

	//! Return whether the format buffer size is
	//! automatically determined.
	bool autoFormatBufferSize () const;

	//! Return number as string using current format
        //! modifiers.
	/*!
          \note For more complicated formating consider
          using the format member function.
         
          \param number - number to be returned as string
         */
	std::string formatNumber (const unsigned long number);

	//! Return number as string using current format
        //! modifiers.
	/*!
          \note For more complicated formating consider	 
          using the format member function.
         
          \param number - number to be returned as string
         */
	std::string formatNumber (const signed long number);

	//! Return number as string using current format
        //! modifiers.
	/*!
          \note For more complicated formating consider	 
          using the format member function.
         
          \param number - number to be returned as string
         */
	std::string formatNumber (const double number);

	//! Append variable to the end of the string.
	/*!
	  After conversion (if desired) append parameter
	  to the end of the string. These functions are
	  called by the << operator.
	 
	  \param v - the variable to be appended
	 */
	void append (const unsigned short v);
	void append (const signed short v);
	void append (const unsigned int v);
	void append (const signed int v);
	void append (const unsigned long v);
	void append (const signed long v);
	void append (const float v);
	void append (const double v);
	void append (const std::string & v);
	void append (const char v);

	//! Append formated string.
	/*!
	  This works like the standart c sprintf function.
	  The maximum length of formated string can be set
	  via the setFormatBufferSize function. On system like
	  linux (GNU) you don't need to care about it since it's
	  handled automatically but on systems that don't have
	  the vsnprintf function the buffer overflow can cause
	  crash. You can query if there is vsnprintf available
	  using macro HAVE_VSNPRINTF or at runtime by calling
	  autoFormatBufferSize ().
	 
	  \param fmt - the string that specifies format
          \sa setFormatBufferSize ()
	  \sa autoFormatBufferSize ()
	 */
	void appendFormat (const char * fmt, ...);

        TextStream & operator<< (const unsigned short v);
	TextStream & operator<< (const signed short v);
        TextStream & operator<< (const unsigned int v);
	TextStream & operator<< (const signed int v);
        TextStream & operator<< (const unsigned long v);
	TextStream & operator<< (const signed long v);
        TextStream & operator<< (const float v);
	TextStream & operator<< (const double v);
        TextStream & operator<< (const std::string & v);
	TextStream & operator<< (const char v);

	bool operator== (const TextStream &stream);

	bool operator== (const std::string &str);

        TextStream & operator= (const TextStream &stream);

	TextStream & operator= (const std::string &str);

	//! Clean the string and reset all modifiers to default values.
        inline void reset ();

	//! Return the current string.
	inline std::string & str () const;

        //! Return current string.
	inline std::string & operator () () const;

}; // TextStream


//////////////////////////////////////////////////////////////
/////////////////////  INLINE FUNCTIONS  /////////////////////
//////////////////////////////////////////////////////////////

inline
TextStream::TextStream ()
{
	m_width = 0;
	m_precision = -1;
	free_string = true;
	m_str = new std::string ();
	m_format_buffer_size = 4096;
}

inline
TextStream::~TextStream ()
{
	if (m_str && free_string)
		delete m_str;
}

inline
TextStream::TextStream (const std::string & str)
{
	m_width = 0;
	m_precision = -1;
	free_string = true;
	m_str = new std::string ();
	*m_str = str;
	m_format_buffer_size = 4096;
}

inline
TextStream::TextStream (std::string * str)
{
	m_width = 0;
	m_precision = -1;
	free_string = false;
	m_str = str;
	m_format_buffer_size = 4096;
}

inline void
TextStream::setWidth (const signed int width)
{
	m_width = width;
}

inline unsigned int
TextStream::width () const
{
	return m_width;
}

inline void
TextStream::setPrecision (const int precision)
{
	m_precision = precision;
}

inline int
TextStream::precision () const
{
	return m_precision;
}

inline void
TextStream::setFormatBufferSize (const int size)
{
	m_format_buffer_size = size;
}

inline int
TextStream::formatBufferSize () const
{
 	return m_format_buffer_size;
}

inline bool
TextStream::autoFormatBufferSize () const
{
#ifdef HAVE_VSNPRINTF
	return 1;
#else
	return 0;
#endif
}

inline std::string
TextStream::formatNumber (const unsigned long number)
{
	int bsize = abs (m_width) > 30 ? abs (m_width) + 5 : 30;
	char buffer[bsize];
	char format[30];
	sprintf (format, "%%%iu", m_width);
	sprintf (buffer, format, number);
	return std::string (buffer);
}

inline std::string
TextStream::formatNumber (const signed long number)
{
	int bsize = abs (m_width) > 30 ? abs (m_width) + 5 : 30;
	char buffer[bsize];
	char format[30];
	sprintf (format, "%%%id", m_width);
	sprintf (buffer, format, number);
	return std::string (buffer);
}

inline std::string
TextStream::formatNumber (const double number)
{
	int bsize = abs (m_width) > 30 ? abs (m_width) + 5 : 30;
	char buffer[bsize];
	char format[30];
	if (m_precision >= 0)
		sprintf (format, "%%%i.%ig", m_width, m_precision);
	else
		sprintf (format, "%%%ig", m_width);
	sprintf (buffer, format, number);
	return std::string (buffer);
}

inline void
TextStream::append (const unsigned short i)
{
	std::string str = formatNumber ((unsigned long) i);
	*m_str += str;
}

inline void
TextStream::append (const signed short i)
{
	std::string str = formatNumber ((signed long) i);
	*m_str += str;
}

inline void
TextStream::append (const unsigned int i)
{
	std::string str = formatNumber ((unsigned long) i);
	*m_str += str;
}

inline void
TextStream::append (const signed int i)
{
	std::string str = formatNumber ((signed long) i);
	*m_str += str;
}

inline void
TextStream::append (const unsigned long i)
{
	std::string str = formatNumber ((unsigned long) i);
	*m_str += str;
}

inline void
TextStream::append (const signed long i)
{
	std::string str = formatNumber ((signed long) i);
	*m_str += str;
}

inline void
TextStream::append (const float f)
{
	std::string str = formatNumber ((double) f);
	*m_str += str;
}

inline void
TextStream::append (const double d)
{
	std::string str = formatNumber ((double) d);
	*m_str += str;
}

inline void
TextStream::append (const std::string & s)
{
	*m_str += s;
}

inline void
TextStream::append (const char c)
{
	*m_str += c;
}

inline void
TextStream::appendFormat (const char * fmt, ...)
{
	char *buffer = new char[formatBufferSize ()];

	int size;

	va_list ap;
	va_start (ap, fmt);
#ifdef HAVE_VSNPRINTF
	size = vsnprintf (buffer, formatBufferSize (), fmt, ap);

	if (size > formatBufferSize ()) {
		// Correct the size of buffer
		delete[] buffer;
                buffer = new char[formatBufferSize ()];
                vsprintf (buffer, fmt, ap);
	}
#else
	size = vsprintf (buffer, fmt, ap);
#endif

	va_end (ap);

        append (buffer);

	if (buffer)
		delete[] buffer;
}

inline TextStream &
TextStream::operator<< (const unsigned short v)
{
	append (v);
	return *this;
}

inline TextStream &
TextStream::operator<< (const signed short v)
{
	append (v);
	return *this;
}

inline TextStream &
TextStream::operator<< (const unsigned int v)
{
	append (v);
	return *this;
}

inline TextStream &
TextStream::operator<< (const signed int v)
{
	append (v);
	return *this;
}

inline TextStream &
TextStream::operator<< (const unsigned long v)
{
	append (v);
	return *this;
}

inline TextStream &
TextStream::operator<< (const signed long v)
{
	append (v);
	return *this;
}

inline TextStream &
TextStream::operator<< (const float v)
{
	append (v);
	return *this;
}

inline TextStream &
TextStream::operator<< (const double v)
{
	append (v);
	return *this;
}

inline TextStream &
TextStream::operator<< (const std::string & v)
{
	append (v);
	return *this;
}

inline TextStream &
TextStream::operator<< (const char v)
{
	append (v);
	return *this;
}

inline bool
TextStream::operator== (const TextStream &stream)
{
	return *m_str == *stream.m_str;
}

inline bool
TextStream::operator== (const std::string &str)
{
	return *m_str == str;
}

inline TextStream &
TextStream::operator= (const TextStream &stream)
{
	*m_str = *stream.m_str;
	return *this;
}

inline TextStream &
TextStream::operator= (const std::string &str)
{
        *m_str = str;
	return *this;
}

inline void
TextStream::reset ()
{
	*m_str = "";
	m_width = 0;
	m_precision = -1;
}

inline std::string &
TextStream::str ()const
{
	return *m_str;
}

inline std::string &
TextStream::operator () () const
{
	return *m_str;
}

} // namespace SEL

#endif // __sel_textstream_h__
